/******************************************************************************
*    Copyright (C), 2022 by D2X studio. All rights reserved.                  *
*                                                                             *
*    @file:      framework.c
*    @owner:     zhengjian.xie@gmail.com                                      *
*    @desc:                                                                   *
*    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +    *
* history:                                                                    *
*  <author>      <time>      <version>              <description>             *
*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  *
*  |  Xzj |  20220412[00:04] | 1.0.0 |                  build              |  *
*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  *
******************************************************************************/
#include "includes.h"
#include <errno.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include <sys/time.h>
#include <sys/types.h>

#define MAX_EVENT_CNT 100
#define MAX_TIMER_CNT 100

/** framework data structure define */
static struct _framework_ {
    uint8_t fdCnt;
    pstEvent evts[MAX_EVENT_CNT];
    uint8_t timerCnt;
    pstTimer timers[MAX_TIMER_CNT];
    bool bye;

} __framework_data__ = {
    0, { 0 }, 0, { 0 }, no
};

static stEvent __framework_evt_exit__ = { -1, yes, nil };
static stEvent __framework_evt_timer__ = { -1, yes, nil };
static pstTimer timers_add[MAX_TIMER_CNT] = { 0 };
static pstTimer timers_del[MAX_TIMER_CNT] = { 0 };

/*
 * =============================================================================
 *                        framework private function
 * =============================================================================
 */

void framework_exit_callback(void* pVoid) {
    __framework_data__.bye = yes;
}

void framework_timer_callback(void* pVoid) {

}

void framework_app_goodbye(void) {
    app_event_del(&__framework_evt_exit__);
    app_event_deinit(&__framework_evt_exit__);
    app_event_del(&__framework_evt_timer__);
    app_event_deinit(&__framework_evt_timer__);

    terminal_deinit();

    printf("framework: goodbye!\n");
}

/*
 * =============================================================================
 *                        framework public function
 * =============================================================================
 */

/** app_init */
void app_init(void) {
    uint8_t i;

    __framework_evt_exit__.doEvent = framework_exit_callback;
    __framework_evt_timer__.doEvent = framework_timer_callback;

    app_event_init(&__framework_evt_exit__);
    app_event_add(&__framework_evt_exit__);
    app_event_init(&__framework_evt_timer__);
    app_event_add(&__framework_evt_timer__);

    terminal_init();

#if defined(TCU_SERVER)
    sock_server_init();
#elif defined(TCU_CLIENT)
    sock_client_init();
    uart_init();
#else
    printf("TCU app say goodbye for you!\n");
#endif

}

/** app_loop */
void app_loop(void) {
    int ret;
    uint8_t i;
    struct timeval timeout;
    fd_set fds_R;

    timeout.tv_sec = 0;
    timeout.tv_usec = 1000;

    do {
        FD_ZERO(&fds_R);
        for ( i = 0; i < __framework_data__.fdCnt; i++ ) {
            FD_SET(__framework_data__.evts[i]->fd, &fds_R);
        }
     
        ret = select(__framework_data__.evts[__framework_data__.fdCnt - 1]->fd + 1,
            &fds_R, nil, nil, &timeout
        );
     
        if ( 0 == ret ) { // timeout
            app_tick();
            continue ;
        }
     
        if ( ret > 0 ) {
            for ( i = 0; i < __framework_data__.fdCnt; i++ ) {
                if ( FD_ISSET(__framework_data__.evts[i]->fd, &fds_R) ) {
                    app_event_reset(__framework_data__.evts[i]);
                    __framework_data__.evts[i]->doEvent(&(__framework_data__.evts[i]));
                    break;
                }
            }
            continue ;
        }
     
        printf("ERR: select error %d\n", errno);
        return ;
    } while ( !(__framework_data__.bye) );

    framework_app_goodbye();
}

/** app_exit */
void app_exit(void) {
    app_event_set(&__framework_evt_exit__);
}

/** app_tick: 1ms */
void app_tick(void) {
    uint8_t i;

    for ( i = 0; i < __framework_data__.timerCnt; i++ ) {
        __framework_data__.timers[i]->interval -= 1;
     
        if (  __framework_data__.timers[i]->interval <= 0 ) {
            __framework_data__.timers[i]->doTimer();
         
            if ( __framework_data__.timers[i]->cycle  ) {
                __framework_data__.timers[i]->interval = __framework_data__.timers[i]->duration;
            }
        }
    }
}

/** app_event_add */
void app_event_add(pstEvent pEvt) {
    uint8_t i;

    if ( !pEvt || pEvt->fd < 0 ) return ;
    if ( __framework_data__.fdCnt >= MAX_EVENT_CNT ) return ;

    // if fd already in it, directly return 
    for ( i = 0; i < __framework_data__.fdCnt; i++ ) {
        if ( pEvt->fd == __framework_data__.evts[i]->fd ) {
            return ;
        }
    }

    // add & sort
    if ( __framework_data__.fdCnt > 0 ) {
        if ( pEvt->fd > __framework_data__.evts[__framework_data__.fdCnt - 1]->fd ) {
            __framework_data__.evts[__framework_data__.fdCnt] = pEvt;
        } else {
            __framework_data__.evts[__framework_data__.fdCnt] = __framework_data__.evts[__framework_data__.fdCnt - 1];
            __framework_data__.evts[__framework_data__.fdCnt - 1] = pEvt;
        }
    } else {
        __framework_data__.evts[0] = pEvt;
    }
   
    __framework_data__.fdCnt += 1;
}

/** app_event_del */
void app_event_del(pstEvent pEvt) {
    uint8_t i;

    if ( !pEvt || pEvt->fd < 0 ) return ;

    for ( i = 0; i < __framework_data__.fdCnt; i++ ) {
        if ( pEvt->fd == __framework_data__.evts[i]->fd ) {
            // del & sort
            if ( i == (__framework_data__.fdCnt - 1) ) {
                __framework_data__.evts[i] = nil;
            } else {
                for ( ; i < __framework_data__.fdCnt; i++ ) {
                    __framework_data__.evts[i] = __framework_data__.evts[i + 1];
                }
            }
            __framework_data__.fdCnt -= 1;
            return ;
        }
    }
}

/** app_event_init */
void app_event_init(pstEvent pEvt) {
    if ( pEvt ) {
        app_event_deinit(pEvt);
        pEvt->fd = eventfd(0, EFD_CLOEXEC);
    }
}

/** app_event_deinit */
void app_event_deinit(pstEvent pEvt) {
    if ( pEvt ) {
        close(pEvt->fd);
        pEvt->fd = -1;
    }
}

/** app_event_set */
void app_event_set(pstEvent pEvt) {
    // uint64_t u = 1;

    if ( pEvt && pEvt->is_eventfd ) {
        //write(pEvt->fd, &u, sizeof(uint64_t));
        eventfd_write(pEvt->fd, 1);
    }
}

/** app_event_reset */
void app_event_reset(pstEvent pEvt) {
    // uint64_t u = 0;

    if ( pEvt && pEvt->is_eventfd ) {
        //write(pEvt->fd, &u, sizeof(uint64_t));
        eventfd_write(pEvt->fd, 0);
    }
}


/** app_timer_add */
void app_timer_add(pstTimer pTimer) {
    uint8_t i;

    for ( i = 0; i < MAX_TIMER_CNT; i++ ) {
        if ( !timers_add[i] ) {
            timers_add[i] = pTimer;
            app_event_set(&__framework_evt_timer__);
            break;
        }
    }
    return ;

    if ( !pTimer ) return ;
    if ( __framework_data__.timerCnt >= MAX_TIMER_CNT ) return ;

    for ( i = 0; i < __framework_data__.timerCnt; i++ ) {
        if ( pTimer == __framework_data__.timers[i] ) {
            return ;
        }
    }

    __framework_data__.timers[__framework_data__.timerCnt - 1] = pTimer;
    __framework_data__.timerCnt += 1;
}

/** app_timer_del */
void app_timer_del(pstTimer pTimer) { // attention: this del timer function has bug, so don't call it anyway
    uint8_t i;

    if ( !pTimer ) return ;

    for ( i = 0; i < __framework_data__.timerCnt; i++ ) {
        if ( pTimer == __framework_data__.timers[i] ) {
            if ( i == (__framework_data__.timerCnt - 1) ) {
                __framework_data__.timers[i] = nil;
            } else {
                for ( ; i < __framework_data__.timerCnt; i++ ) {
                    __framework_data__.timers[i] = __framework_data__.timers[i + 1];
                }
            }
            __framework_data__.timerCnt -= 1;
            return ;
        }
    }
}
